package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.LoginDto;
import com.zzyl.entity.User;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.mapper.UserMapper;
import com.zzyl.properties.JwtTokenProperties;
import com.zzyl.service.LoginService;
import com.zzyl.utils.JwtUtil;
import com.zzyl.vo.LoginVo;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {

    final UserMapper userMapper;
    final ResourceMapper resourceMapper;
    final JwtTokenProperties jwtTokenProperties;
    final StringRedisTemplate stringRedisTemplate;

    /**
     * 后端用户登录
     *
     * @param dto
     * @return
     */
    @Override
    public LoginVo login(LoginDto dto) {

        //1. 先根据用户名查询数据库
        User user = userMapper.selectByUsername(dto.getUsername());
        if (ObjectUtil.isEmpty(user)) {
            throw new BaseException(BasicEnum.INCORRECT_PASSWORD);
        }


        //TODO 2. 判断redis是否有用户的密码错误次数
        String redisKey = "user:password:error:count:" + user.getId();
        String redisData = stringRedisTemplate.opsForValue().get(redisKey);
        redisData = StrUtil.nullToDefault(redisData, "0");
        int count = Integer.parseInt(redisData);
        //次数大于3，则表示用户已被冻结
        if (count >= 3) {
            throw new RuntimeException("密码错误次数已达上限，账号已被冻结，请稍后再试！");
        }


        //3. 比对密码是否正确
        boolean check = DigestUtil.bcryptCheck(dto.getPassword(), user.getPassword());
        if (!check) {
            //TODO 记录错误次数，将数据存到redis(userId, +1, 过期时间)
            stringRedisTemplate.opsForValue().set(redisKey, String.valueOf(++count), 6, TimeUnit.HOURS);

            throw new BaseException(BasicEnum.INCORRECT_PASSWORD);
        }


        stringRedisTemplate.delete(redisKey);

        //4. 判断用户状态是否正常
        check = StrUtil.equals(user.getDataState(), "1");//数据状态（0正常 1停用）
        if (check) {
            throw new BaseException(BasicEnum.ACCOUNT_DISABLED);
        }


        //TODO 5. 获取当前用户对应的资源列表
        List<String> urlList = resourceMapper.selectListByUserId(user.getId()); //user_role、role_resource、resource
        //存储到redis
        stringRedisTemplate.opsForValue().set(CacheConstants.USER_ACCESS_URLS + user.getId(),
                JSONUtil.toJsonStr(urlList));


        //6. 给用户生成token令牌（jwt中只存放用户id）
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("id", user.getId()); //存放用户id

        //生成jwt令牌
        String jwt = JwtUtil.createJWT(jwtTokenProperties.getSecretKey(),
                jwtTokenProperties.getTtl(),
                claims);

        //7. 封装vo返回数据
        LoginVo loginVo = BeanUtil.toBean(user, LoginVo.class);
        loginVo.setUserToken(jwt);

        return loginVo;
    }
}












